package com.atwei.mango.mybatis;

import com.alibaba.druid.util.StringUtils;
import com.atwei.mango.mybatis.annotation.DataFilter;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.io.StringReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @Classname DataScopeStatementHandler
 * @Description mybatis自定义数据权限插件
 * @Date 2022/5/11 14:41
 * @Author liuwei
 */
@Intercepts(
        {
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        }
)
public class DataScopeStatementHandler implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //获取拦截下的mapper
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        //获取注解对象
        DataFilter dataFilter = getDataFilter(mappedStatement);
        //没有注解直接放行
        if (null == dataFilter){
            return invocation.proceed();
        }
        //获取原始sql语句
        BoundSql boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
        String sql = boundSql.getSql();
        String newSql = modifySqlByUtil(sql,dataFilter);
        //通过反射修改sql语句
        Field sqlSourceField = mappedStatement.getClass().getDeclaredField("sqlSource");
        sqlSourceField.setAccessible(true);
        Object sqlSource = sqlSourceField.get(mappedStatement);
        Field boundSqlField = sqlSource.getClass().getDeclaredField("sqlSource");
        boundSqlField.setAccessible(true);
        Object boundSqlObj = boundSqlField.get(sqlSource);
        Field sqlField = boundSqlObj.getClass().getDeclaredField("sql");
        sqlField.setAccessible(true);
        sqlField.set(boundSqlObj,newSql);
        return invocation.proceed();
    }

    /**
     * @Description 根据注解信息处理sql
     * @Author liuwei
     * @Date 2022/5/12 9:12
     * @params [java.lang.String, com.atwei.mango.mybatisPlugins.DataFilter]
     * @return java.lang.String
     **/
    @Deprecated
    private String modifySql(String orgSql,DataFilter dataFilter){
        //处理sql语句
        String newSql;
        if (orgSql.contains("where")){
            newSql = orgSql.replace("where",
                    "where "+dataFilter.userFiled()+" = 'admin' and ");
        }else{
            if (orgSql.contains("order")){
                newSql = orgSql.replace("order",
                        "where "+dataFilter.userFiled()+" = 'admin' order");

            }else if (orgSql.contains("limit")){
                newSql = orgSql.replace("limit",
                        "where "+dataFilter.userFiled()+" = 'admin' limit");
            }else if (orgSql.contains("LIMIT")){
                newSql = orgSql.replace("LIMIT",
                        "where "+dataFilter.userFiled()+" = 'admin' LIMIT");
            }else{
                newSql = orgSql +
                        " where "+dataFilter.userFiled()+" = 'admin'";
            }
        }
        return newSql;
    }
    /**
     * @Description 根据注解信息处理sql
     * @Author liuwei
     * @Date 2022/5/12 9:12
     * @params [java.lang.String, com.atwei.mango.mybatisPlugins.DataFilter]
     * @return java.lang.String
     **/
    private String modifySqlByUtil(String orgSql,DataFilter dataFilter) throws JSQLParserException {
        //处理sql语句
        String authUser = dataFilter.userFiled();
        String authDept = dataFilter.deptFiled();
        if (StringUtils.isEmpty(authDept)&&StringUtils.isEmpty(authUser)){
            return orgSql;
        }
//        //使用CCJSqlParserUtil对sql进行解析
//        //参考mybatis-plus源码{@link com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor#autoCountSql}
//        CCJSqlParserManager parserManager = new CCJSqlParserManager();
//        Select select = (Select) parserManager.parse(new StringReader(orgSQql));
//        PlainSelect plain = (PlainSelect) select.getSelectBody();
//        Table fromItem = (Table) plain.getFromItem();
//        //有别名用别名，无别名用表名，防止字段冲突报错
//        String aliasTableName = fromItem.getAlias() == null ? fromItem.getName() : fromItem.getAlias().getName();
//        //对authSql中的占位符进行替换
//        authSql = authSql.replace(TABLE_NAME, aliasTableName).replace(USER_ID, MOCK_USERID).replace(DEPT_ID, MOCK_DEPT_ID);
//        if (plain.getWhere() == null) {
//            plain.setWhere(CCJSqlParserUtil.parseCondExpression(authSql));
//        } else {
//            plain.setWhere(new AndExpression(plain.getWhere(), CCJSqlParserUtil.parseCondExpression(authSql)));
//        }
//————————————————
//        版权声明：本文为CSDN博主「小菜鸟俊隽」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
//        原文链接：https://blog.csdn.net/qq_42795049/article/details/123462091
        CCJSqlParserManager parserManager = new CCJSqlParserManager();
        Select select = (Select) parserManager.parse(new StringReader(orgSql));
        PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
        if (null == plainSelect.getWhere()){
            plainSelect.setWhere(CCJSqlParserUtil.parseCondExpression(authUser+" = 'admin'"));
        }else{
            plainSelect.setWhere(new AndExpression(plainSelect.getWhere(),CCJSqlParserUtil.parseCondExpression(authUser+" = 'name'")));
        }

        return plainSelect.toString();
    }


    /**
     * @Description 通过反射获取mapper方法是否添加了数据拦截注解
     * @Author liuwei
     * @Date 2022/5/12 9:02
     * @params [org.apache.ibatis.mapping.MappedStatement]
     * @return com.atwei.mango.mybatisPlugins.DataFilter
     **/
    private DataFilter getDataFilter(MappedStatement mappedStatement) throws ClassNotFoundException {
        DataFilter dataFilter = null;
        String id = mappedStatement.getId();
        String className = id.substring(0,id.lastIndexOf("."));
        String methodName = id.substring(id.lastIndexOf(".")+1);
        final Class<?> cls = Class.forName(className);
        final Method[] methods = cls.getMethods();

        for (Method method : methods) {
            if (method.getName().equals(methodName) && method.isAnnotationPresent(DataFilter.class)){
                dataFilter = method.getAnnotation(DataFilter.class);
                break;
            }
        }
        return dataFilter;
    }

}
